﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using SimpleSpreadsheet.Controls;
using SimpleSpreadsheet.ViewModels;

namespace SimpleSpreadsheet.Views
{
    /// <summary>
    /// Interaction logic for WorksheetView.xaml
    /// </summary>
    public partial class WorksheetView : WorkBaseControl
    {
        public WorksheetView()
        {
            InitializeComponent();
            BackgroundOperationCompleted += OnCompleted;
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
            ErrorMessageDisplayed = false;
        }

        public WorksheetViewModel WorksheetViewModel { get; private set; }
        private ContentControl SelectedCell { get; set; }
        protected bool ErrorMessageDisplayed { get; set; }

        /// <summary>
        /// Handles the end of operation running in background
        /// </summary>
        /// <param name="e"></param>
        private void OnCompleted(RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                ErrorMessageDisplayed = true;
                throw e.Error;
            }

            SelectedCell.Focus();
        }

        /// <summary>
        /// Sets the currently selected cell
        /// </summary>
        /// <param name="sender"></param>
        private void SetCurrentCell(object sender)
        {
            var el = sender as ContentControl;
            if (el != null)
            {
                BindingExpression bindingExpression = el.GetBindingExpression(ContentProperty);
                if (bindingExpression != null)
                {
                    // This is actually a cell, but View does not know about it
                    object cell = bindingExpression.DataItem;

                    // Update selected cell in view model
                    var worksheetViewModel = GetViewModel() as WorksheetViewModel;
                    if (worksheetViewModel != null)
                    {
                        if (cell != null)
                            worksheetViewModel.SelectCurrentCell(cell);

                        CurrentCell.Text = worksheetViewModel.CurrentCell.Text;
                    }

                    // Update textbox value
                    SelectedCell = el;
                }
            }
        }

        /// <summary>
        /// Handle Enter key pressed in CurrentCell textbox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CurrentCell_EnterKeyPressed(object sender, KeyEventArgs e)
        {
            if (!ErrorMessageDisplayed)
            {
                var tb = sender as TextBox;
                if (tb != null)
                {
                    var cellControl = SelectedCell as CellControl;
                    InvokeOperation(UpdateViewModel, tb.Text, cellControl.RowNumber,
                                    cellControl.ColumnNumber);
                }
            }
            else
            {
                ErrorMessageDisplayed = false;
            }
        }

        /// <summary>
        /// Updates the underlying ViewModel (invoked in background)
        /// </summary>
        /// <param name="oViewModel"></param>
        /// <param name="args"></param>
        private void UpdateViewModel(object oViewModel, params object[] args)
        {
            var text = args[0] as string;
            var cellRow = (int) args[1];
            var cellColumn = (int) args[2];

            var worksheetViewModel = oViewModel as WorksheetViewModel;
            if (worksheetViewModel != null)
            {
                worksheetViewModel.UpdateWorksheet(text, cellRow, cellColumn);
            }
        }

        /// <summary>
        /// Selects the first cell when control is loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            var cp = Cells.ItemContainerGenerator.ContainerFromIndex(0) as ContentPresenter;
            var cell = FindVisualChild<CellControl>(cp);
            if (cell != null)
            {
                cell.Focus();
            }
        }

        /// <summary>
        /// Updates the textbox when cell is focused
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_GotFocus(object sender, RoutedEventArgs e)
        {
            SetCurrentCell(sender);
        }

        /// <summary>
        /// Handles keyboard input in cells
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cells_KeyUp(object sender, KeyEventArgs e)
        {
            // If letter or digit is pressed, go to edit mode
            if (((e.Key >= Key.A && e.Key <= Key.Z) || (e.Key >= Key.D0 && e.Key <= Key.D9) ||
                 (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)))
            {
                var converter = new KeyConverter();
                string inputSymbol = converter.ConvertToString(e.Key);
                CurrentCell.Text = inputSymbol;
                CurrentCell.Focus();
                CurrentCell.CaretIndex = 1;
            }
                // F2 moves focus to textbox
            else if (e.Key == Key.F2)
            {
                CurrentCell.Focus();
                CurrentCell.CaretIndex = CurrentCell.Text.Length;
            }
                // Delete removes the content of currently selected cell and updates the ViewModel
            else if (e.Key == Key.Delete)
            {
                var cell = SelectedCell as CellControl;
                if (cell != null)
                {
                    CurrentCell.Text = String.Empty;
                    InvokeOperation(UpdateViewModel, String.Empty, cell.RowNumber, cell.ColumnNumber);
                }
            }
        }

        /// <summary>
        /// Handles all uncatched/rethrown exceptions
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            string errorText = e.Exception.InnerException == null
                                   ? e.Exception.Message
                                   : String.Format("{0} ({1})", e.Exception.Message, e.Exception.InnerException.Message);
            ErrorMessageDisplayed = true;
            MessageBox.Show(errorText, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            e.Handled = true;
        }
    }
}